revision:
Click an image to open the lightbox; wheel and drag to pan while enlarged.










code:
<div class="gallery" id="gallery">
<div><img src="../images/1.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/1a.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2a.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3a.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4a.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5a.jpg" class="image" alt="holiday" width="100%" height="100%"/></div>
</div>
<div id="lightbox">
<span id="close">×</span>
<div id="zoom-container">
<img id="zoom-image" src="" alt="Zoomable">
</div>
</div>
<style>
/* Gallery */
.gallery { display: flex; flex-wrap: wrap; gap: 0.8vw; padding: 1.2vw; max-width: 90VW; margin: 0 auto; width: 90vw;}
.gallery img { width: 16vw; height: 14vw; object-fit: cover; cursor: pointer; border: 0.1vw solid #ddd;border-radius: 0.3vw; transition: border-color 0.2s;}
.gallery img:hover { border-color: #007bff;}
/* Lightbox */
#lightbox { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.9); z-index: 1000; justify-content: center;
align-items: center;}
#lightbox.active {display: flex;}
#zoomContainer { width: 50vw; height: 50vh; overflow: hidden; position: relative; cursor: grab;}
#zoomContainer.dragging {cursor: grabbing;}
#zoomImage {position: absolute; top: 0; left: 0; transition: transform 0.1s ease; user-select: none; max-width: none; max-height: none; }
/* Close button */
#close {position: absolute; top: 2vw; right: 3vw;color: white;font-size: 3vw; cursor: pointer; }
</style>
<script>
// Get DOM elements
const lightbox = document.getElementById('lightbox');
const zoomImage = document.getElementById('zoom-image');
const zoomContainer = document.getElementById('zoom-container');
const closeBtn = document.getElementById('close');
// Zoom/pan state
let scale = 1;
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
let startX, startY, startOffsetX, startOffsetY;
const minScale = 1;
const maxScale = 5;
const zoomIntensity = 0.1;
// Attach click listeners to all gallery images
document.querySelectorAll('.gallery div img').forEach(thumb => {
thumb.addEventListener('click', () => {
const fullSrc = thumb.dataset.full || thumb.src;
openLightbox(fullSrc);
});
});
// Open lightbox
function openLightbox(src) {
zoomImage.src = src;
scale = 1;
offsetX = 0;
offsetY = 0;
lightbox.classList.add('active');
setTimeout(updateTransform, 10);
}
// Close lightbox
closeBtn.addEventListener('click', () => {
lightbox.classList.remove('active');
});
// Prevent closing when clicking image
zoomContainer.addEventListener('click', (e) => e.stopPropagation());
// Drag to pan
zoomContainer.addEventListener('mousedown', (e) => {
if (e.button !== 0) return;
isDragging = true;
zoomContainer.classList.add('dragging');
startX = e.clientX;
startY = e.clientY;
startOffsetX = offsetX;
startOffsetY = offsetY;
e.preventDefault();
});
window.addEventListener('mousemove', (e) => {
if (!isDragging || !lightbox.classList.contains('active')) return;
offsetX = startOffsetX + (e.clientX - startX);
offsetY = startOffsetY + (e.clientY - startY);
updateTransform();
});
window.addEventListener('mouseup', () => {
isDragging = false;
zoomContainer.classList.remove('dragging');
});
// Zoom with wheel
zoomContainer.addEventListener('wheel', (e) => {
if (!lightbox.classList.contains('active')) return;
e.preventDefault();
const zoom = e.deltaY > 0 ? -zoomIntensity : zoomIntensity;
const newScale = Math.min(Math.max(scale + zoom, minScale), maxScale);
if (newScale === scale) return;
const rect = zoomContainer.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
offsetX = (mouseX - offsetX) * (newScale / scale) + offsetX - mouseX;
offsetY = (mouseY - offsetY) * (newScale / scale) + offsetY - mouseY;
scale = newScale;
updateTransform();
});
// Double-click to zoom
zoomContainer.addEventListener('dblclick', (e) => {
if (!lightbox.classList.contains('active')) return;
const rect = zoomContainer.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const targetScale = scale >= 2 ? minScale : 2;
offsetX = (mouseX - offsetX) * (targetScale / scale) + offsetX - mouseX;
offsetY = (mouseY - offsetY) * (targetScale / scale) + offsetY - mouseY;
scale = targetScale;
updateTransform();
});
// Apply transform & constrain panning
function updateTransform() {
if (!zoomImage.naturalWidth) return;
const imgW = zoomImage.naturalWidth;
const imgH = zoomImage.naturalHeight;
const contW = zoomContainer.clientWidth;
const contH = zoomContainer.clientHeight;
const maxOffsetX = (imgW * scale - contW) / 2;
const maxOffsetY = (imgH * scale - contH) / 2;
offsetX = Math.max(-maxOffsetX, Math.min(maxOffsetX, offsetX));
offsetY = Math.max(-maxOffsetY, Math.min(maxOffsetY, offsetY));
zoomImage.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
}
// Update when image loads
zoomImage.onload = updateTransform;
</script>
wheel and drag to pan while enlarged.










code:
<div class="gallery1" id="gallery1">
<div class="image-container"><img src="../images/1.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/2.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/3.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/4.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/5.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/1a.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/2a.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/3a.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/4a.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
<div class="image-container"><img src="../images/5a.jpg" class="zoomable-image" alt="holiday" width="100%" height="100%"/></div>
</div>
<style>
/* Gallery */
.gallery1 { display: flex; flex-wrap: wrap; gap: 0.8vw; padding: 1.2vw; max-width: 90VW; margin: 0 auto; width: 90vw;}
.image-container {width: 16vw; height: 14vw; overflow: hidden; border: 0.1vw solid #ccc; position: relative; cursor: grab;}
.image-container.dragging {cursor: grabbing;}
.zoomable-image {position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; transition: transform 0.1s ease; user-select: none;
/* We'll control scale/position via JS */}
</style>
<script>
// For each image container, attach zoom/pan behavior
document.querySelectorAll('.image-container').forEach(container => {
const img = container.querySelector('.zoomable-image');
let scale = 1;
let offsetX = 0;
let offsetY = 0;
let isDragging = false;
let startX, startY, startOffsetX, startOffsetY;
const minScale = 1;
const maxScale = 3;
const zoomIntensity = 0.1;
// Initial style
img.style.transform = 'translate(0,0) scale(1)';
// Mouse down to start drag
container.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // left click only
isDragging = true;
container.classList.add('dragging');
startX = e.clientX;
startY = e.clientY;
startOffsetX = offsetX;
startOffsetY = offsetY;
e.preventDefault();
});
// Mouse move = pan
window.addEventListener('mousemove', (e) => {
if (!isDragging) return;
offsetX = startOffsetX + (e.clientX - startX);
offsetY = startOffsetY + (e.clientY - startY);
applyTransform();
});
// Mouse up = stop drag
window.addEventListener('mouseup', () => {
isDragging = false;
container.classList.remove('dragging');
});
// Zoom with wheel
container.addEventListener('wheel', (e) => {
e.preventDefault();
const zoom = e.deltaY > 0 ? -zoomIntensity : zoomIntensity;
const newScale = Math.min(Math.max(scale + zoom, minScale), maxScale);
if (newScale === scale) return;
// Zoom toward mouse position inside container
const rect = container.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
// Adjust offset so point under cursor stays fixed
offsetX = (mouseX - offsetX) * (newScale / scale) + offsetX - mouseX;
offsetY = (mouseY - offsetY) * (newScale / scale) + offsetY - mouseY;
scale = newScale;
applyTransform();
});
// Optional: double-click to reset
container.addEventListener('dblclick', () => {
scale = 1;
offsetX = 0;
offsetY = 0;
applyTransform();
});
function applyTransform() {
// Get container and natural image dimensions
const contWidth = container.clientWidth;
const contHeight = container.clientHeight;
const imgWidth = img.naturalWidth;
const imgHeight = img.naturalHeight;
// Max allowable offset (so image doesn't show empty space)
const maxOffsetX = (imgWidth * scale - contWidth) / 2;
const maxOffsetY = (imgHeight * scale - contHeight) / 2;
// Constrain panning
offsetX = Math.max(-maxOffsetX, Math.min(maxOffsetX, offsetX));
offsetY = Math.max(-maxOffsetY, Math.min(maxOffsetY, offsetY));
// Apply
img.style.transform = `translate(${offsetX}px, ${offsetY}px) scale(${scale})`;
}
// Initialize on load
if (img.complete) {
applyTransform();
} else {
img.onload = applyTransform;
}
});
</script>
Click an image to enlarge it. Drag to pan while enlarged.
Note: Enlarging will push other content. Click enlarged image to shrink.










code:
<div class="gallery2" id="gallery2">
<div><img src="../images/1.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/1a.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2a.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3a.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4a.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5a.jpg" class="interactive-img" alt="holiday" width="100%" height="100%"/></div>
</div>
<style>
.gallery2 {display: flex; flex-wrap: wrap; gap: 2vw; margin: 2vw 0; }
.interactive-img {/* Base style */ width: 16vw; height: 28vh; cursor: pointer; transition: width 0.3s ease, box-shadow 0.3s ease; position: relative; user-select: none; }
.interactive-img.enlarged {width: 32vw; height: 38vh; /* or use max-width + auto height */ box-shadow: 0 0 15px rgba(0,0,0,0.3); z-index: 10; /* Optional: slight elevation */ margin: 0vw 0; }
/* Instructions */
.hint { color: #666; font-style: italic; margin-top: 0.8vw;}
</style>
<script>
document.querySelectorAll('.interactive-img').forEach(img => {
let isEnlarged = false;
let positionX = 0;
let positionY = 0;
let isDragging = false;
let startX, startY;
// Toggle enlarge on click
img.addEventListener('click', (e) => {
if (!isEnlarged) {
// Enlarge
img.classList.add('enlarged');
img.style.position = 'relative';
img.style.cursor = 'grab';
img.style.transform = 'translate(0, 0)';
isEnlarged = true;
} else {
// Shrink back
img.classList.remove('enlarged');
img.style.cursor = 'pointer';
img.style.transform = '';
positionX = 0;
positionY = 0;
isEnlarged = false;
}
e.stopPropagation();
});
// Start drag
img.addEventListener('mousedown', (e) => {
if (!isEnlarged || e.button !== 0) return;
isDragging = true;
img.style.cursor = 'grabbing';
startX = e.clientX;
startY = e.clientY;
e.preventDefault();
});
// Pan while dragging
window.addEventListener('mousemove', (e) => {
if (!isDragging || !isEnlarged) return;
const dx = e.clientX - startX;
const dy = e.clientY - startY;
positionX += dx;
positionY += dy;
img.style.transform = `translate(${positionX}px, ${positionY}px)`;
startX = e.clientX;
startY = e.clientY;
});
// Stop drag
window.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
if (isEnlarged) img.style.cursor = 'grab';
}
});
// Optional: zoom with wheel while enlarged
img.addEventListener('wheel', (e) => {
if (!isEnlarged) return;
e.preventDefault();
// For simplicity, we won't scale further — just pan
// (You could add dynamic scaling if needed)
});
});
</script>
Click an image to enlarge it. Drag to pan while enlarged.










code:
<div class="gallery3" id="gallery3">
<div><img src="../images/1.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/1a.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/2a.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/3a.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/4a.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
<div><img src="../images/5a.jpg" class="thumbnail" alt="holiday" width="100%" height="100%"/></div>
</div>
<div id="lightbox1" class="lightbox1 hidden1">
<div class="lightbox-content1">
<button class="close1">×</button>
<div class="pan-container1">
<img src="image.jpg" id="zoom-image1" alt="Zoomable1">
</div>
</div>
</div>
<style>
.gallery3 {display: flex; flex-wrap: wrap; gap: 2vw; margin: 2vw 0;}
.thumbnail {width: 16vw; height: 28vh; cursor: pointer; position: relative;max-width: 20vw;}
.lightbox1 {position: fixed; top: 0; left: 0; width: 50%; height: 50%; background: rgba(0, 0, 0, 0.8); z-index: 1000; display: flex; justify-content: center; align-items: center;}
.lightbox1.hidden1 {display: none;}
.lightbox-content1 {position: relative; max-width: 90vw; max-height: 90vh; overflow: hidden;}
.pan-container1 {overflow: hidden; cursor: grab; max-width: 100%; max-height: 100%; display: flex; justify-content: center; align-items: center; }
.pan-container1 img {transition: transform 0.1s; max-width: none; max-height: none; }
.close1 {position: absolute; top: 1vw; right: 1.5vw; background: white; border: none; font-size: 1.5vw; cursor: pointer; border-radius: 50%; width: 2.2vw; height: 2.4vh; display: flex; align-items: center; justify-content: center; }
#zoom-image1{width: 40vw; height: 60vh; top: 2vw; left: 4vw;}
</style>
<script>
document.querySelectorAll('.thumbnail').forEach(thumb => {
thumb.addEventListener('click', () => {
const lightbox = document.getElementById('lightbox1');
const img = lightbox.querySelector('#zoom-image1');
img.src = thumb.src;
lightbox.classList.remove('hidden1');
initPanZoom(img);
});
});
document.querySelector('.close1').addEventListener('click', () => {
document.getElementById('lightbox1').classList.add('hidden1');
});
// Close lightbox on backdrop click (optional but user-friendly)
document.getElementById('lightbox1').addEventListener('click', (e) => {
if (e.target === e.currentTarget) {
e.currentTarget.classList.add('hidden1');
}
});
function initPanZoom(img) {
const container = img.parentElement;
let scale = 1;
let posX = 0, posY = 0;
let isPanning = false;
let startX, startY, startPX, startPY;
// Reset state
img.style.transform = 'translate(0,0) scale(1)';
scale = 1;
posX = 0;
posY = 0;
container.style.cursor = 'grab';
// Disable image drag (prevents native drag behavior)
img.draggable = false;
// --- Wheel Zoom ---
container.addEventListener('wheel', (e) => {
e.preventDefault();
const delta = e.deltaY > 0 ? 0.9 : 1.1;
const newScale = Math.min(Math.max(0.5, scale * delta), 5);
// Compute zoom point relative to container
const containerRect = container.getBoundingClientRect();
const mouseX = e.clientX - containerRect.left;
const mouseY = e.clientY - containerRect.top;
// Adjust pan so zoom centers on mouse
const zoomFactor = newScale / scale;
posX = mouseX - (mouseX - posX) * zoomFactor;
posY = mouseY - (mouseY - posY) * zoomFactor;
scale = newScale;
applyTransform();
}, { passive: false });
// --- Mouse Panning ---
container.addEventListener('mousedown', (e) => {
if (e.button !== 0) return; // only left mouse
isPanning = true;
startX = e.clientX;
startY = e.clientY;
startPX = posX;
startPY = posY;
container.style.cursor = 'grabbing';
e.preventDefault(); // Prevent text/image selection
});
window.addEventListener('mousemove', (e) => {
if (!isPanning) return;
posX = startPX + (e.clientX - startX);
posY = startPY + (e.clientY - startY);
applyTransform();
});
window.addEventListener('mouseup', () => {
isPanning = false;
container.style.cursor = 'grab';
});
// --- Touch Panning ---
let touchStartX, touchStartY;
container.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
isPanning = true;
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
startPX = posX;
startPY = posY;
e.preventDefault();
}
});
container.addEventListener('touchmove', (e) => {
if (isPanning && e.touches.length === 1) {
const dx = e.touches[0].clientX - touchStartX;
const dy = e.touches[0].clientY - touchStartY;
posX = startPX + dx;
posY = startPY + dy;
applyTransform();
e.preventDefault();
}
});
container.addEventListener('touchend', () => {
isPanning = false;
});
// Apply CSS transform
function applyTransform() {
img.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
}
}
</script>